package com.shengqin.controller;

import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.hash.Hash;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shengqin.annotations.OperaLog;
import com.shengqin.calculate.data.DaWeiApiModel;
import com.shengqin.calculate.events.DaWeiApiUploadEvent;
import com.shengqin.common.pojo.CommonPage;
import com.shengqin.common.pojo.CommonResult;
import com.shengqin.config.CommonException;
import com.shengqin.entity.Classes;
import com.shengqin.entity.PlanStudent;
import com.shengqin.entity.Student;
import com.shengqin.enums.BusinessType;
import com.shengqin.listeners.StudentDataListener;
import com.shengqin.pojo.common.DeleteParam;
import com.shengqin.pojo.student.StudentImportParam;
import com.shengqin.pojo.student.StudentParam;
import com.shengqin.pojo.student.UpdateGradeParam;
import com.shengqin.service.IClassesService;
import com.shengqin.service.IFileService;
import com.shengqin.service.IPlanStudentService;
import com.shengqin.service.IStudentService;
import com.shengqin.service.impl.FaceFeatureService;
import com.shengqin.utils.MultipartFileUtil;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生信息 前端控制器
 * </p>
 *
 * @author Rui.Zhou
 * @since 2023-12-05
 */
@RestController
@RequestMapping("/admin/v1/student")
@Slf4j
public class StudentController {

    @Resource
    private IStudentService studentService;
    @Resource
    private IPlanStudentService planStudentService;
    @Resource
    private IClassesService classesService;
    @Resource
    private IFileService fileService;
    @Value("${upload.path}")
    private String uploadPath;

    @Value("${upload.minio.enabled:false}")
    private Boolean minioEnabled;

    @Value("${ai-ocr.enabled:false}")
    private Boolean aiOcrEnabled;
    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private FileStorageService fileStorageService;
    @Resource
    private FaceFeatureService faceFeatureService;

    @Operation(summary = "一键升班")
    @PostMapping("/updateGrade")
    public CommonResult<Boolean> updateGrade(@RequestBody UpdateGradeParam updateGradeParam) {
        if (updateGradeParam.getSchoolId() == null) {
            throw new CommonException("请选择学校");
        }
        Set<String> excludeStudyCodes = new HashSet<>();
        if (StringUtils.hasLength(updateGradeParam.getExcludeStudyCodeStr())) {
            String excludeStudyCode = updateGradeParam.getExcludeStudyCodeStr().replace("，", ",");
            String[] codes = excludeStudyCode.split(",");
            excludeStudyCodes = Arrays.stream(codes).collect(Collectors.toSet());
        }
        Set<String> includeStudyCodes = new HashSet<>();
        if (StringUtils.hasLength(updateGradeParam.getIncludeStudyCodeStr())) {
            String includeStudyCode = updateGradeParam.getIncludeStudyCodeStr().replace("，", ",");
            String[] codes = includeStudyCode.split(",");
            includeStudyCodes = Arrays.stream(codes).collect(Collectors.toSet());
        }
        List<Student> studentList = studentService.listBySchoolIdAndExcludeStudyCodes(updateGradeParam.getSchoolId(), includeStudyCodes, excludeStudyCodes);

        List<Classes> classesList = new ArrayList<>();

        studentList.forEach(student -> {
            Classes classes = new Classes();
            if (student.getGradeId().equals(44)) {
                student.setGraduateFlag(true);
                student.setGraduateYear(String.valueOf(DateUtil.year(new Date())));
                student.setGradeId(99);
            } else if (student.getGradeId().equals(43)) {
                student.setGraduateFlag(false);
                student.setGraduateYear(null);
                student.setGradeId(44);
                classes.setGradeId(44);
                classes.setId(student.getClassesId());
            } else if (student.getGradeId().equals(42)) {
                student.setGraduateFlag(false);
                student.setGraduateYear(null);
                student.setGradeId(43);
                classes.setGradeId(44);
                classes.setId(student.getClassesId());
            } else if (student.getGradeId().equals(41)) {
                student.setGraduateFlag(false);
                student.setGraduateYear(null);
                student.setGradeId(42);
                classes.setGradeId(44);
                classes.setId(student.getClassesId());
            }
            classesList.add(classes);
        });

        if (!CollectionUtils.isEmpty(classesList)) {
            classesService.saveOrUpdateBatch(classesList);
        }

        return CommonResult.data(studentService.updateBatchById(studentList));
    }

    @Operation(summary = "转班")
    @PostMapping("/changeClasses")
    public CommonResult<Boolean> changeClasses(@RequestBody StudentParam studentParam) {
        if (CollectionUtils.isEmpty(studentParam.getStudentIds())) {
            throw new CommonException("请选择学生");
        }
        Classes classes = classesService.getById(studentParam.getClassesId());
        List<Student> studentList = studentService.listByIds(studentParam.getStudentIds());
        studentList.forEach(student -> {
            student.setClassesName(classes.getName());
            student.setClassesCode(classes.getCode());
            student.setClassesId(classes.getId());
            student.setGradeId(classes.getGradeId());
        });
        return CommonResult.data(studentService.updateBatchById(studentList));
    }

    @PostMapping("/photoImport")
    public CommonResult photoImport(@RequestPart("file") MultipartFile file) {
        if (StringUtils.hasLength(file.getOriginalFilename()) && !file.getOriginalFilename().endsWith(".zip")) {
            return CommonResult.error("仅支持上传.zip文件");
        }
        try {
            File outFile = new File(uploadPath + "/avatar");
            File unzipPath = new File(uploadPath + "/temp/" + System.currentTimeMillis());
            if (!unzipPath.exists()) {
                unzipPath.mkdirs();
            }
            File photoZipFile = ZipUtil.unzip(file.getInputStream(), unzipPath, StandardCharsets.UTF_8);
            getPhotoFile(photoZipFile, outFile);
        } catch (IOException e) {
            log.error("上传头像错误", e);
            throw new CommonException("上传头像失败" + e.getMessage());
        }
        return CommonResult.ok().setMsg("上传成功，图片数据正在异步处理，请稍后查看");
    }

    private void getPhotoFile(File photoFile, File outFile) {
        if (photoFile.exists()) {
            if (photoFile.isDirectory()) {
                if (photoFile.listFiles() != null) {
                    File[] photoFiles = photoFile.listFiles();
                    if (photoFiles != null) {
                        for (File photoChildFile : photoFiles) {
                            if (photoChildFile.exists()) {
                                if (photoChildFile.isDirectory()) {
                                    getPhotoFile(photoChildFile, outFile);
                                } else {
                                    uploadFile(photoChildFile, outFile);
                                }
                            }
                        }
                    }
                }
            } else {
                if (photoFile.isFile()) {
                    uploadFile(photoFile, outFile);
                }
            }
        }
    }

    public String getFileExtension(String filename) {
        // 正则表达式匹配文件扩展名
        Pattern pattern = Pattern.compile("\\.(\\w+)$");
        Matcher matcher = pattern.matcher(filename);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return "";
    }

    private void uploadFile(File photoFile, File outFile) {
        FileUtil.copy(photoFile, outFile, true);
        String studyCode = photoFile.getName().substring(0, photoFile.getName().lastIndexOf("."));
        if (StringUtils.hasLength(studyCode)) {
            Flowable.just(studyCode).subscribe(sc -> {
                if (aiOcrEnabled != null && aiOcrEnabled) {
                    DaWeiApiModel daWeiApiModel = new DaWeiApiModel();
                    daWeiApiModel.setStudyCode(sc);
                    daWeiApiModel.setFile(photoFile);
                    applicationContext.publishEvent(new DaWeiApiUploadEvent(this, daWeiApiModel));
                }
                String url = "";
                if (minioEnabled) {
                    FileInfo fileInfo = fileStorageService.of(photoFile)
                            .setPath("avatar/")
                            .setSaveFilename(studyCode + "." + getFileExtension(photoFile.getName())).upload();
                    url = fileInfo.getUrl();
                } else {
                    url = fileService.storageFileWithReturnUrl("avatar", MultipartFileUtil.fileToMultiPartFile(photoFile));
                }
                log.info("{}上传头像url,{}", sc, url);
                planStudentService.updateAvatar(sc, url);
            });
        }
    }

    public String multipartFileToBase64(MultipartFile file) throws IOException {
        byte[] fileBytes = file.getBytes();
        return Base64.getEncoder().encodeToString(fileBytes);
    }

    @PostMapping("/photoUpload")
    public CommonResult<String> photoUpload(@RequestPart("file") MultipartFile file, @RequestPart("studyCode") String studyCode) throws IOException {
        File outFile = new File(uploadPath + "/avatar/" + file.getOriginalFilename());
        if (outFile.exists()) {
            outFile.delete();
        }
        String url = "";
        if (minioEnabled) {
            FileInfo fileInfo = fileStorageService.of(file)
                    .setPath("avatar/")
                    .setSaveFilename(studyCode + "." + getFileExtension(file.getOriginalFilename())).upload();
            url = fileInfo.getUrl();
            try {
                faceFeatureService.getFaceFaceture(studyCode, multipartFileToBase64(file), null);
            } catch (Exception e) {
                log.error("提取人脸你特征报错", e);
            }
        } else {
            Files.copy(file.getInputStream(), Paths.get(uploadPath + "/avatar/" + file.getOriginalFilename()));
            url = fileService.storageFileWithReturnUrl("avatar", file);
        }
        log.info("上传头像url,{}", url);
        return CommonResult.data(url);
    }

    @Operation(summary = "导入学生")
    @PostMapping("import")
    public CommonResult importStudentFile(@RequestPart("file") MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), StudentImportParam.class, new StudentDataListener(studentService)).sheet().doRead();
        } catch (ExcelDataConvertException e) {
            String message = String.format("第%s行，第%s列解析异常，请修改后重新上传", e.getRowIndex() + 1, e.getColumnIndex() + 1);
            throw new CommonException(message);
        } catch (Exception e) {
            throw new CommonException(e.getMessage());
        }
        return CommonResult.ok().setMsg("导入成功");
    }

    @PostMapping("page")
    public CommonResult<Page<Student>> getPage(@RequestBody CommonPage<Student> pageParam) {
        return CommonResult.data(studentService.getPage(pageParam));
    }

    @OperaLog(title = "保存学生信息", businessType = BusinessType.UPDATE)
    @PostMapping("save")
    public CommonResult<Boolean> onSave(@RequestBody Student student) {
        if (student.getId() == null) {
            Student studentByStudyCode = studentService.getByStudyCode(student.getStudyCode());
            if (studentByStudyCode != null) {
                throw new CommonException("学籍号已存在，无法添加！");
            }
            student.setCreateTime(LocalDateTime.now());
        } else {
            Student oldStudent = studentService.getById(student.getId());
            if (!oldStudent.getStudyCode().equals(student.getStudyCode())) {
                Student studentByStudyCode = studentService.getByStudyCode(student.getStudyCode());
                if (studentByStudyCode != null) {
                    throw new CommonException("学籍号已存在，无法添加！");
                }
            }
            student.setUpdateTime(LocalDateTime.now());
        }

        Classes classes = classesService.getById(student.getClassesId());
        student.setClassesCode(classes.getId().toString());
        student.setClassesName(classes.getName());
        student.setSchoolId(classes.getSchoolId());

        List<PlanStudent> planStudents = planStudentService.listByStudyCode(student.getStudyCode());
        if(!CollectionUtils.isEmpty(planStudents)){
            planStudents.forEach(ps-> {
                Integer planStudentId = ps.getId();
                BeanUtils.copyProperties(student,ps);
                ps.setId(planStudentId);
            });
            planStudentService.updateBatchById(planStudents);
        }

        return CommonResult.data(studentService.saveOrUpdate(student));
    }

    @OperaLog(title = "删除学生", businessType = BusinessType.DELETE)
    @PostMapping("delete")
    public CommonResult<Boolean> onDelete(@RequestBody DeleteParam deleteParam) {
        return CommonResult.data(studentService.removeById(deleteParam.getId()));
    }

    @PostMapping("batchDelete")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> batchDelete(@RequestBody DeleteParam deleteParam) {
        if (CollectionUtils.isEmpty(deleteParam.getIds())) {
            throw new CommonException("请先选择要删除的数据！");
        }
        List<Student> studentList = studentService.listByIds(deleteParam.getIds());
        Set<String> studyCodes = studentList.stream().map(Student::getStudyCode).collect(Collectors.toSet());
        studentService.removeByIds(deleteParam.getIds());
        boolean deleted = planStudentService.removeByStudyCodes(studyCodes);
        return CommonResult.data(deleted);
    }
}
